package com.zzyl.nursing.service.impl;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzyl.common.constant.HttpStatus;
import com.zzyl.common.core.page.TableDataInfo;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.utils.DateUtils.LocalDateTimeResult;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.common.utils.UserThreadLocal;
import com.zzyl.nursing.domain.Elder;
import com.zzyl.nursing.domain.FamilyMember;
import com.zzyl.nursing.domain.Reservation;
import com.zzyl.nursing.dto.ReservationDto;
import com.zzyl.nursing.dto.ReservationPageDto;
import com.zzyl.nursing.mapper.ElderMapper;
import com.zzyl.nursing.mapper.FamilyMemberMapper;
import com.zzyl.nursing.mapper.ReservationMapper;
import com.zzyl.nursing.service.IReservationService;
import com.zzyl.nursing.vo.ReservationCountVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.zzyl.common.utils.DateUtils.getLocalDateResult;
import static com.zzyl.common.utils.DateUtils.getLocalDateTimeResult;

/**
 * 预约信息Service业务层处理
 *
 * @author alexis
 * @date 2025-06-04
 */
@Service
public class ReservationServiceImpl extends ServiceImpl<ReservationMapper, Reservation> implements IReservationService {

    @Autowired
    private ReservationMapper reservationMapper;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    // 使用用户ID作为key前缀
    private static final String REDIS_KEY = "user:cancelCount";

    @Autowired
    private FamilyMemberMapper familyMemberMapper;

    @Autowired
    private ElderMapper elderMapper;

    /**
     * 新增预约信息
     *
     * @param reservation 预约信息
     */
    @Override
    public void insertReservation(Reservation reservation) {
        // 1.获取当前用户id
        Long userId = UserThreadLocal.getUserId();
        if (ObjectUtil.isEmpty(userId)) {
            throw new BaseException("请先登录");
        }

        // 2.判断手机号是否预约过此时间段
        FamilyMember familyMember =
                familyMemberMapper.selectOne(new LambdaQueryWrapper<FamilyMember>().eq(FamilyMember::getId, userId));
        String phone = familyMember.getPhone();

        // 根据手机号查询预约信息
        Long count = lambdaQuery().eq(Reservation::getMobile, phone)
                                  .eq(Reservation::getTime, reservation.getTime())
                                  .eq(Reservation::getStatus, 0)
                                  .count();
        if (count > 0) {
            throw new BaseException("您已预约过此时间段，请勿重复预约");
        }

        // 3.判断家人是否已经退住
        Long count1 = elderMapper.selectCount(
                new LambdaQueryWrapper<Elder>().eq(Elder::getName, reservation.getVisitor()).eq(Elder::getStatus, 5));
        if (count1 > 0) {
            throw new BaseException("家人已退住，不能预约");
        }

        // 4.查询当前用户取消预约次数
        Integer cancelCount = (Integer) redisTemplate.opsForHash().get(REDIS_KEY, userId.toString());
        cancelCount = ObjectUtil.defaultIfNull(cancelCount, 0);

        if (cancelCount > 3) {
            throw new BaseException("您已取消预约3次，不能再预约了");
        }

        // 5.设置预约状态为待报道
        reservation.setStatus(0);

        // 7.保存预约信息
        save(reservation);
    }


    /**
     * 取消预约
     *
     * @param id 预约信息主键
     */
    @Override
    public void cancel(Long id) {
        // 1. 验证用户和预约信息
        Long userId = UserThreadLocal.getUserId();
        if (ObjectUtil.isEmpty(userId)) {
            throw new BaseException("请先登录");
        }

        Reservation reservation = getById(id);
        if (ObjectUtil.isEmpty(reservation)) {
            throw new BaseException("预约信息不存在");
        }

        // 2. 获取当前取消次数
        Integer cancelCount = ObjectUtil.defaultIfNull(
                (Integer) redisTemplate.opsForHash().get(REDIS_KEY, userId.toString()),
                0
                                                      );

        // 3. 如果是第一次取消，设置第二天凌晨过期
        if (cancelCount == 0) {
            LocalDateTime now = LocalDateTime.now();
            // 计算第二天凌晨的时间
            LocalDateTime expireTime = now.plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
            // 计算剩余秒数
            long seconds = Duration.between(now, expireTime).getSeconds();
            redisTemplate.expire(REDIS_KEY, seconds, TimeUnit.SECONDS);
        }

        // 4. 更新取消次数
        redisTemplate.opsForHash().put(REDIS_KEY, userId.toString(), cancelCount + 1);

        // 5. 更新预约状态
        reservation.setStatus(2);
        updateById(reservation);
    }


    /**
     * 查询取消预约数量
     *
     * @return 取消预约数量
     */
    @Override
    public Long getCancelledReservationCount() {
        // 1.获取当前用户id
        Long userId = UserThreadLocal.getUserId();

        // 2.获取当天开始时间和结束时间
        LocalDateTimeResult result = getLocalDateTimeResult();

        return lambdaQuery().eq(Reservation::getStatus, 2)
                            .eq(Reservation::getUpdateBy, userId)
                            .between(Reservation::getUpdateTime, result.getStartTime(), result.getEndTime()).count();
    }

    /**
     * 分页查询预约信息列表
     *
     * @return 预约信息集合
     */
    @Override
    public TableDataInfo<Reservation> selectPage(ReservationDto reservationDto) {
        TableDataInfo<Reservation> tableDataInfo = new TableDataInfo<>();

        Page<Reservation> page = new Page<>(reservationDto.getPageNum(), reservationDto.getPageSize());

        lambdaQuery()
                .eq(reservationDto.getStatus() != null, Reservation::getStatus, reservationDto.getStatus())
                .orderByDesc(Reservation::getCreateTime).page(page);

        tableDataInfo.setCode(HttpStatus.SUCCESS);
        tableDataInfo.setMsg("请求成功");
        tableDataInfo.setRows(page.getRecords());
        tableDataInfo.setTotal(page.getTotal());

        return tableDataInfo;
    }

    /**
     * 查询剩余预约次数
     *
     * @return 预约数量
     */
    @Override
    public List<ReservationCountVo> countByTime(Long time) {
        // 1.将时间戳转为LocalDateTime
        LocalDateTime ldt = LocalDateTimeUtil.of(time);
        LocalDateTime startTime = ldt.toLocalDate().atStartOfDay();
        LocalDateTime endTime = startTime.plusDays(1);

        // 2.查询剩余预约数量
        return reservationMapper.countByTime(startTime, endTime);
    }

    /**
     * 定时更新预约状态
     */
    @Override
    @Transactional
    public void updateReservationStatus() {
        // 1.查询状态为0的预约信息
        List<Reservation> list = lambdaQuery().eq(Reservation::getStatus, 0).list();

        // 2.没有在预约的时间来访，那这次预约就会失效
        list.forEach(reservation -> {
            if (reservation.getTime().isBefore(LocalDateTimeUtil.now())) {
                // 更新预约状态为过期3
                reservation.setStatus(3);
            }
        });

        // 3.批量更新预约信息
        updateBatchById(list);
    }

    /**
     * 查询预约信息列表
     *
     * @param reservationPageDto 查询参数
     * @return 预约信息集合
     */
    @Override
    public List<Reservation> selectReservationList(ReservationPageDto reservationPageDto) {

        LocalDateTimeResult result =
                reservationPageDto.getTime() != null ? getLocalDateResult(reservationPageDto.getTime()) :
                        null;

        LambdaQueryChainWrapper<Reservation> query = lambdaQuery()
                .like(
                        StringUtils.hasText(reservationPageDto.getName()), Reservation::getName,
                        reservationPageDto.getName()
                     )
                .eq(
                        StringUtils.hasText(reservationPageDto.getMobile()), Reservation::getMobile,
                        reservationPageDto.getMobile()
                   );

        if (result != null) {
            query.between(Reservation::getTime, result.getStartTime(), result.getEndTime());
        }

        return query.orderByDesc(Reservation::getTime).orderByDesc(Reservation::getCreateTime).list();
    }

    /**
     * 删除预约信息
     *
     * @param id 预约信息id
     */
    @Override
    public void removeReservationById(Long id) {
        // 根据id查询预约信息
        Reservation reservation = getById(id);

        // 预约信息未完成，不能删除
        if (reservation.getStatus() == 0) {
            throw new BaseException("预约信息未完成，不能删除");
        }

        // 删除预约信息
        removeById(id);
    }

    /**
     * 定时清空超过一周的预约信息
     */
    @Override
    public void clearReservation() {
        LocalDateTime seventeenDaysAgo = LocalDateTime.now().minusDays(7);
        lambdaUpdate().lt(Reservation::getTime, seventeenDaysAgo).ne(Reservation::getStatus, 0).remove();
    }

    /**
     * 更新预约信息
     */
    @Override
    public void updateReservationById(Reservation reservation) {
        // 如果预约状态不是待报道，则不允许更新
        if (!Integer.valueOf(0).equals(reservation.getStatus())) {
            throw new BaseException("预约状态不是待报道，不允许更新");
        }
        updateById(reservation);
    }
}
